<?php

namespace app\service\tool;

use alexandernst\devicedetect\DeviceDetect;
use app\models\cacheModel\SettingModel;
use app\service\LinkPagerService;
use app\service\UploadFileService;
use yii\base\Model;
use yii\data\Pagination;
use yii\helpers\StringHelper;

/**
 * 文件工具类
 * Class GenerateService
 * @package app\models
 */
class UtilsService extends Model
{

    /**
     * 判断：是否为手机端访问
     * @return DeviceDetect
     */
    public static function isMobile()
    {

        $model = new DeviceDetect();
        $model->isMobile();

        return $model->isMobile();
    }

    /**
     * 判断：手机号是否合法
     * @param $phone
     * @return bool
     */
    public static function isMobileNumber($phone)
    {
        $chars = "/^1[3-9][0-9]{1}[0-9]{8}$/";
        return preg_match($chars, $phone);
    }

    /**
     * 获取：前台模板基础文件夹
     * @param bool $device 强制设备，false：根据ua自动段判断，pc: 电脑端，mobile: 手机端
     * @return string
     */
    public static function getFrontTempFolder($device = false)
    {
        // 基础路径 + 皮肤文件夹
        $path = '@template/' . SettingModel::getSetting('skin');
        // 判断皮肤类型是否自适应
        if (SettingModel::getSetting('skin_type') != 1) {
            // 非自适应增加当前设备类型文件夹
            $path .= '/' . (self::isMobile() || $device == 'mobile' ? 'mobile' : 'pc');
        }
        return $path;
    }

    /**
     * 获取：[Model]返回的错误
     * @param array $error 错误数组
     * @param int $customCode 自定义指定[Code]
     * @param int $customColumn 自定义指定[字段]
     * @return array|null
     */
    public static function getModelError($error = [], $customCode = false, $customColumn = false)
    {

        // 系统错误码
        $systemCode = $customCode ? $customCode : 500;
        // 系统错误字段名
        $systemColumn = $customColumn ? $customColumn : 'unknown';
        // 系统错误提示
        $systemHint = '请求频繁稍，请后尝试';

        // 空的错误信息|错误信息长度小于等于 0
        if (empty($error) || count($error) <= 0) return [
            'msg'        => $systemHint,
            'code'       => $systemCode,
            'column'     => $systemColumn,
            'error_hint' => '未知错误'
        ];

        // 不是数组 塑造数组
        if (!is_array($error)) $error = [
            $systemColumn => [$error]
        ];

        $transitErr = [];
        foreach ($error as $k => $v) {

            // 还是数组取出最后一个操作
            if (is_array($v)) $v = end($v);

            $numKey = floatval($k);
            // 不是数组做返回
            $transitErr['msg']        = $k >= $systemCode && !$customCode ? $systemHint : self::getChineseErr($v);
            $transitErr['code']       = is_numeric($k) && $numKey != 500 ? $k : $systemCode;
            $transitErr['column']     = is_numeric($k) ? $systemColumn : $k;
            $transitErr['error_hint'] = self::getChineseErr($v);

            // 需要自定义[code]
            if ($customCode) $transitErr['code'] = $customCode;
        }

        return $transitErr;
    }

    /**
     * 获取：[Model]返回的错误中文化
     * @param string $error 要替换的字符串
     * @return mixed|string
     */
    public static function getChineseErr($error = '')
    {

        // 不是字符串直接返回
        $parIsStr = false;
        if (is_string($error)) {
            $error    = [$error];
            $parIsStr = true;
        }

        $replace = [
            ' cannot be blank'        => '不能为空',
            ' must be an integer'     => '必须是一个数字',
            ' must be a string'       => '必须是一条字符串',
            ' has already been taken' => '已经存在了'
        ];

        foreach ($error as $k => $v) {

            // 还是数组则是多维数组 在执行下
            if (is_array($v)) $v = self::getChineseErr($v);

            $error[$k] = str_replace(array_keys($replace), array_values($replace), $v);
        }

        return $parIsStr ? end($error) : $error;
    }

    /**
     * 执行：塑造[MODEL]错误信息
     * @param string $attribute
     * @param string $error
     * @return array
     */
    public static function renderModelError($attribute = '', $error = '')
    {

        $errors               = [];
        $errors[$attribute][] = $error;

        return $errors;
    }

    /**
     * 根据大写字母分割字符串
     * @param string $str 操作字符
     * @param bool $isSmall 是否小些化
     * @return array[]|false|string|string[]
     */
    public static function splitUpperCase($str = '', $isSmall = true)
    {

        $str = preg_split('/(?=[A-Z])/', $str, -1, PREG_SPLIT_NO_EMPTY);

        // 需要转为小写
        if ($isSmall) foreach ($str as $k => $v) {
            // 转为小写
            $str[$k] = strtolower($v);
        }

        return $str;
    }

    /**
     * 获取分页[HTML]
     * @param int $pageSize 每页展示数据数
     * @param int $count 总数
     * @param array $opt 配置
     *  ` maxButtonCount: 最大按钮数|指的是1,2,3,4这些按钮
     *  ` loadGoPage: 加载输入页面跳转
     * @return string|null
     */
    public static function getPageHtml($pageSize = 0, $count = 0, $opt = [])
    {

        try {

            // 分页信息
            $page = new Pagination([
                //总的记录条数
                'totalCount'    => $count,
                //分页大小
                'pageSize'      => $pageSize,
                //设置地址栏当前页数参数名
                'pageParam'     => 'page',
                //设置地址栏分页大小参数名
                'pageSizeParam' => false
            ]);

            return LinkPagerService::widget([
                'pagination'     => $page,
                'loadGoPage'     => empty($opt['loadGoPage']) || !$opt['loadGoPage'] ? false : true,
                'firstPageLabel' => '首页',
                'lastPageLabel'  => '尾页',
                'nextPageLabel'  => '下一页',
                'prevPageLabel'  => '上一页',
                // 分页按钮数
                'maxButtonCount' => empty($opt['maxButtonCount']) ? 5 : $opt['maxButtonCount'],
                //设置class样式
                'options'        => ['class' => 'page'],
            ]);

        } catch (\Exception $error) {
            return null;
        }
    }

    /**
     * 数组(二维数组)列表合并(为一维数组)
     * @param array $arrays
     * @return array
     * example: [1 => [1,2], 2 => [3,4]] => [1,2,3,4]
     */
    public static function arrayListMerge($arrays = [])
    {

        if (empty($arrays)) return $arrays;
        $ends = [];
        foreach ($arrays as $k => $v) {
            $ends = array_merge($ends, $v);
        }
        return $ends;
    }

    /**
     * [where]条件过滤某字段
     * @param $where
     * @param $field
     * @return array
     */
    public static function whereRm($where, $field)
    {

        if (empty($where)) return $where;
        // 条件强制数组
        $where = !is_array($where) ? [$where] : $where;
        $count = 1;
        foreach ($where as $k => $v) {
            // 还是数组 - 执行
            if (is_array($v) && is_array(array_shift($v))) {
                $where[$k] = self::whereRm($v, $field);
                break;
            }

            // 非数组 检测过滤
            if (is_string($v) && strval($k) != $field) continue;
            // 数组 检测过滤
            if (is_array($v) && array_shift($v) != $field) continue;

            // 循环不超100
            if ($count >= 100) break;

            unset($where[$k]);
            $count = $count + 1;
        }

        return $where;
    }

    /**
     * [百度]站长工具连接提交
     * @param array $url
     * @return bool|string
     */
    public static function submitBaiDuSpider($url = [])
    {

        $urls    = is_string($url) ? explode(',', $url) : $url;
        $api     = 'http://data.zz.baidu.com/urls?site=https://www.haodexx.com&token=YAQcbSmzR8B58v91';
        $ch      = curl_init();
        $options = array(
            CURLOPT_URL            => $api,
            CURLOPT_POST           => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POSTFIELDS     => implode("\n", $urls),
            CURLOPT_HTTPHEADER     => array('Content-Type: text/plain'),
        );
        curl_setopt_array($ch, $options);
        $result = curl_exec($ch);
        return $result;
    }

    /**
     * 下载远程图片到指定(项目内)文件夹
     * @param string $remoteUrl 远程图片地址
     * @param string $savePath 保存地址
     * @param string $delPath 上传结果删除路径
     * @return bool|array
     */
    public static function downImage($remoteUrl, $savePath, $delPath = '')
    {

        // 路径转义
        $savePath = str_replace('\\', '/', $savePath);

        ### 扩展名验证 && 替换
        // 远程文件扩展名
        $fileExt = pathinfo($remoteUrl, PATHINFO_EXTENSION);
        // 没有扩展名则为[jpg]图片
        if (empty($fileExt) || !UploadFileService::isAllowTypes($fileExt)) $fileExt = 'jpg';
        // 验证下图片头部大小信息
        $fp        = fopen($remoteUrl, 'rb');
        $bin       = fread($fp, 2); // 只读2个字节
        $str_info  = @unpack("C2chars", $bin);
        $type_code = intval($str_info['chars1'] . $str_info['chars2']);
        // 根据[即将上传的文件类型]、[当前文件头大小]取出合法的文件类型
        $fileType = UploadFileService::getTypeSizeForType('image', $type_code);
        // 未找到就是不合法
        if (!$fileType) {

            \Yii::error([
                "`````````````````````````````````````````````````````````",
                "``                   远程文件下载失败                     ``",
                "`` 错误详情: 远程图片类型{$fileExt}非法                    ``",
                "`` 保存地址：{$savePath}                                 ``",
                "`` 图片地址: {$remoteUrl}                                ``",
                "`` 图片头大小:{$type_code}                                ``",
                "`````````````````````````````````````````````````````````",
            ]);
            return false;
        }
        // 需要替换扩展名
        if (strstr($savePath, '{EXT}')) {
            // 替换扩展名
            $savePath = str_replace('.{EXT}', '.' . $fileExt, $savePath);
            $savePath = str_replace('{EXT}', '.' . $fileExt, $savePath);
        }

        // 文件夹不存在 新建
        if (!file_exists($savePath)) FileService::mkdir(StringHelper::dirname($savePath));

        file_put_contents($savePath, file_get_contents($remoteUrl));

        // 保存文件
        return [
            'ext'           => $fileExt,
            'name'          => pathinfo($savePath, PATHINFO_FILENAME) . '.' . $fileExt,
            'absolute_path' => $savePath,
            'relative_path' => str_replace(\Yii::getAlias($delPath), '', $savePath)
        ];
    }

    /**
     * 字符串截取
     * @param string $str 字符串
     * @param int $start 从第几个字符截取
     * @param int $length 截取几个字符
     * @param string $suffix 末尾字符
     * @param string $charset 编码
     * @return string
     */
    public static function cutString($str, $start = 0, $length = 0, $suffix = "...",
                                     $charset = "utf-8")
    {
        if (function_exists("mb_substr")) {
            $slice = mb_substr($str, $start, $length, $charset);
            return $slice == $str ? $slice : $slice . $suffix;
        } else if (function_exists('iconv_substr')) {
            $slice = iconv_substr($str, $start, $length, $charset);
            return $slice == $str ? $slice : $slice . $suffix;
        }

        $re['utf-8']  = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/";
        $re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/";
        $re['gbk']    = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/";
        $re['big5']   = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/";
        preg_match_all($re[$charset], $str, $match);
        $slice = join("", array_slice($match[0], $start, $length));
        return $slice == $str ? $slice : $slice . $suffix;
    }

    /**
     * 验证身份证号是否合法
     * @param $idCard
     * @return bool
     */
    public static function checkIdCard($idCard)
    {
        $idCard = strtoupper($idCard);
        if (!preg_match('#^\d{17}(\d|X)$#', $idCard)) {
            return false;
        }
        // 判断出生年月日的合法性(解决号码为666666666666666666也能通过校验的问题)
        $birth = substr($idCard, 6, 8);
        if ($birth < "19000101" || $birth > date("Ymd")) {
            return false;
        }
        $year  = substr($birth, 0, 4);
        $month = substr($birth, 4, 2);
        $day   = substr($birth, 6, 2);
        if (!checkdate($month, $day, $year)) {
            return false;
        }
        // 校验身份证格式(mod11-2)
        $check_sum = 0;
        for ($i = 0; $i < 17; $i++) {
            // $factor = (1 << (17 - $i)) % 11;
            $check_sum += $idCard[$i] * ((1 << (17 - $i)) % 11);
        }
        $check_code = (12 - $check_sum % 11) % 11;
        $check_code = $check_code == 10 ? 'X' : strval($check_code);
        if ($check_code !== substr($idCard, -1)) {
            return false;
        }
        return true;
    }

    /**
     * 去除全部空格
     * @param $str
     * @return string
     */
    public static function removeSpace($str) {
        if (!is_string($str) || !is_int($str)) return $str;
        $search = array(' ', '　', 'ㅤ', ' ', '	', '  ');
        $replace = '';
        $str = str_replace($search, $replace, $str);
        $str = trim($str);
        return $str;
    }

}
